/*
 * 使用cJSON库、MQTT协议以及特定的硬件API来实现一个物联网设备
 */

#include <hi_task.h>
#include <string.h>
#include <hi_wifi_api.h>
#include <hi_mux.h>
#include <hi_io.h>
#include <hi_gpio.h>
#include "iot_config.h"
#include "iot_log.h"
#include "iot_main.h"
#include "iot_profile.h"
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "app_aht20.h"
#include "app_mq2.h"
#include "uart_control.h"

/* attribute initiative to report */
#define TAKE_THE_INITIATIVE_TO_REPORT                                   //是否主动上报数据
#define ONE_SECOND                          (1000)                      //一秒的时间间隔
/* oc request id */
#define CN_COMMADN_INDEX                    "commands/request_id="      //定义了一个字符串常量，表示命令的请求ID
#define WECHAT_SUBSCRIBE_LIGHT              "light"                     //定义了一个字符串常量，表示订阅的主题为 "light"
#define WECHAT_SUBSCRIBE_LIGHT_ON_STATE     "1"                         //定义了一个字符串常量，表示灯的开启状态为 "1"
#define WECHAT_SUBSCRIBE_LIGHT_OFF_STATE    "0"                         //定义了一个字符串常量，表示灯的关闭状态为 "0"

int g_ligthStatus = -1;                                 //用于存储灯的状态。初始值为 -1
typedef void (*FnMsgCallBack)(hi_gpio_value val);       //函数指针类型，指向一个返回类型为 void、参数类型为 hi_gpio_value 的函数
extern int c_T;
extern int c_H;
extern int c_C;
extern unsigned char d_d;


typedef struct FunctionCallback {                       //FunctionCallback结构体类型
    hi_bool  stop;                  //表示停止标志
    hi_u32 conLost;                 //表示连接丢失
    hi_u32 queueID;                 //表示队列ID
    hi_u32 iotTaskID;               //表示任务ID
    FnMsgCallBack    msgCallBack;   //存储回调函数
}FunctionCallback;
FunctionCallback g_functinoCallback;//FunctionCallback 类型的全局变量，用于存储函数回调的相关信息

/* CPU Sleep time Set 设置 CPU 休眠时间 */
unsigned int TaskMsleep(unsigned int ms)
{
    if (ms <= 0) {  //如果 ms 小于等于 0，函数会返回错误码 HI_ERR_FAILURE
        return HI_ERR_FAILURE;
    }               //否则，会调用 hi_sleep 函数休眠指定的毫秒数，并返回成功状态
    return hi_sleep((hi_u32)ms);
}

/* 初始化设备的配置 */
static void DeviceConfigInit(hi_gpio_value val)
{
    hi_io_set_func(HI_IO_NAME_GPIO_9, HI_IO_FUNC_GPIO_9_GPIO);  //调用 hi_io_set_func 函数将 GPIO 9 的功能设置为 GPIO 模式
    hi_gpio_set_dir(HI_GPIO_IDX_9, HI_GPIO_DIR_OUT);            //用 hi_gpio_set_dir 函数将 GPIO 9 的方向设置为输出方向
    hi_gpio_set_ouput_val(HI_GPIO_IDX_9, val);                  //调用 hi_gpio_set_output_val 函数将 GPIO 9 的输出值设置为 val
}

/* 注册设备消息的回调函数 */
static int  DeviceMsgCallback(FnMsgCallBack msgCallBack)
{
    g_functinoCallback.msgCallBack = msgCallBack;
    //将参数 msgCallBack 赋值给全局变量 g_functinoCallback.msgCallBack，即将传入的回调函数绑定到全局变量上
    return 0;
}

/* 通过微信控制设备的消息处理 */
static void wechatControlDeviceMsg(hi_gpio_value val)
{
    DeviceConfigInit(val);
    /* 通过调用 DeviceConfigInit 函数来对设备进行配置初始化 */
}

// < this is the callback function, set to the mqtt, and if any messages come, it will be called
//  这是回调函数，设置给mqtt，如果有任何消息到来，它将被调用
// < The payload here is the json string
//  这里的有效载荷是json字符串
static void DemoMsgRcvCallBack(int qos, const char *topic, const char *payload)
{
    IOT_LOG_DEBUG("RCVMSG:QOS:%d TOPIC:%s PAYLOAD:%s\r\n", qos, topic, payload);
    /* 云端下发命令后，板端的操作处理 */
    if (strstr(payload, WECHAT_SUBSCRIBE_LIGHT) != NULL) {
        if (strstr(payload, WECHAT_SUBSCRIBE_LIGHT_OFF_STATE) != NULL) {
            wechatControlDeviceMsg(HI_GPIO_VALUE1);
            g_ligthStatus = HI_FALSE;
        } else {
            wechatControlDeviceMsg(HI_GPIO_VALUE0);
            g_ligthStatus = HI_TRUE;
        }
    }
    return HI_NULL;
}

/* publish sample */
hi_void IotPublishSample(void)
{
    /* reported attribute */
    WeChatProfile weChatProfile = {
        .subscribeType = "type",
        .status.subState = "state",
        .status.subReport = "reported",
        .status.reportVersion = "version",
        .status.Token = "clientToken",
        /* report motor */
        .reportAction.subDeviceActionMotor = "motor",
        .reportAction.motorActionStatus = d_d, /* 0 : motor off */
        /* report temperature */
        .reportAction.subDeviceActionTemperature = "temperature",
        .reportAction.temperatureData = c_T, /* 30 :temperature data */
        /* report humidity */
        .reportAction.subDeviceActionHumidity = "humidity",
        .reportAction.humidityActionData = c_H, /* humidity data */
        /* report light_intensity */
        .reportAction.subDeviceActionLightIntensity = "light_intensity",
        .reportAction.lightIntensityActionData = c_C, /* 60 : light_intensity */
    };

    /* report light */
    if (g_ligthStatus == HI_TRUE) {
        weChatProfile.reportAction.subDeviceActionLight = "light";
        weChatProfile.reportAction.lightActionStatus = 1; /* 1: light on */
    } else if (g_ligthStatus == HI_FALSE) {
        weChatProfile.reportAction.subDeviceActionLight = "light";
        weChatProfile.reportAction.lightActionStatus = 0; /* 0: light off */
    } else {
        weChatProfile.reportAction.subDeviceActionLight = "light";
        weChatProfile.reportAction.lightActionStatus = 0; /* 0: light off */
    }
    /* profile report */
    IoTProfilePropertyReport(CONFIG_USER_ID, &weChatProfile);
}

// < this is the demo main task entry,here we will set the wifi/cjson/mqtt ready and
// < wait if any work to do in the while
hi_void *DemoEntry(const char *arg)
{
    WifiStaReadyWait();
    cJsonInit();
    IoTMain();
    /* 云端下发回调 */
    IoTSetMsgCallback(DemoMsgRcvCallBack);
    /* 主动上报 */
#ifdef TAKE_THE_INITIATIVE_TO_REPORT
    while (1) {
        /* 用户可以在这调用发布函数进行发布，需要用户自己写调用函数 */
        IotPublishSample(); // 发布例程
        usleep(10000); // 休眠10ms
#endif
        TaskMsleep(ONE_SECOND);
    }
    return NULL;
}

//创建线程
// < This is the demo entry, we create a task here,
// and all the works has been done in the demo_entry
#define CN_IOT_TASK_STACKSIZE  0x1000   //分配给任务堆栈的字节数，使用十六进制表示，等于4096字节
#define CN_IOT_TASK_PRIOR 25            //任务的优先级
#define CN_IOT_TASK_NAME "IOTDEMO"      //任务的名字

//负责配置和创建一个新的线程
static void AppDemoIot(void)
{
    osThreadAttr_t attr;      //负责配置和创建一个新的线程
    // IoTWatchDogDisable();     //禁止看门狗定时器。避免在调试过程中因为较长的中断或停滞而导致系统重置

    attr.name = "IOTDEMO";
    attr.attr_bits = 0U;      //线程属性的位字段，这里设置为0U，表示没有特别的属性
    //线程控制块的内存和大小，这里都设置为NULL和0U，表示不使用预分配的内存块
    attr.cb_mem = NULL;
    attr.cb_size = 0U;

    attr.stack_mem = NULL;                      //线程控制块的内存和大小，这里都设置为NULL和0U，表示不使用预分配的内存块
    attr.stack_size = CN_IOT_TASK_STACKSIZE;    //线程堆栈的大小
    attr.priority = 24;          //线程的优先级  线程的优先级范围是9到38

    //建一个新的线程
    if (osThreadNew((osThreadFunc_t)DemoEntry, NULL, &attr) == NULL) {      
        printf("[mqtt] Falied to create IOTDEMO!\n");
    }
}

//在系统启动时自动执行AppDemoIot函数
SYS_RUN(AppDemoIot);